﻿Imports System.Web
Imports Comms.PushUpgrade.Interface

Public MustInherit Class CUpgradeClient : Inherits CClient_RestOrWcf : Implements IUpgrade

#Region "Constructors"
    Protected Sub New(hostName As String)
        Me.New(hostName, Config_.UseSsl, Config_)
    End Sub
    Protected Sub New(hostName As String, useSsl As Boolean)
        Me.New(hostName, useSsl, Config_)
    End Sub
    Protected Sub New(hostName As String, useSsl As Boolean, password As IConfig_Client)
        MyBase.New(hostName, useSsl, password, EGzip.None, ESerialisation.Protobuf) 'Protobuf serialisation, and no compression (unless otherwise specified)
    End Sub
#End Region

#Region "Shared - Factory (Config-based Rest vs WCF)"
    Private Const INPROC_ERROR As String = "For InProc option, add reference to Server-side, call Factory method on the Server class (add if necessary, using interface as return type)"
    Private Const CMD_LINE_ERR As String = "No implementation for CmdLine exe"
    Public Shared Function Factory() As CUpgradeClient
        Select Case Config_.Transport
            Case ETransport.Rest : Return New CUpgradeClient_Rest
            Case ETransport.WCF : Return New CUpgradeClient_Wcf

            Case ETransport.CmdLineExe : Throw New Exception(CMD_LINE_ERR)
            Case ETransport.InProcess : Throw New Exception(INPROC_ERROR)
        End Select
        Throw New Exception("Not implemented: " & Config_.Transport.ToString)
    End Function
    Public Shared Function Factory(hostName As String, useSsl As Boolean) As CUpgradeClient
        Select Case Config_.Transport
            Case ETransport.Rest : Return New CUpgradeClient_Rest(hostName, useSsl)
            Case ETransport.WCF : Return New CUpgradeClient_Wcf(Config_.Endpoint, hostName, useSsl)

            Case ETransport.CmdLineExe : Throw New Exception(CMD_LINE_ERR)
            Case ETransport.InProcess : Throw New Exception(INPROC_ERROR)
        End Select
        Throw New Exception("Not implemented: " & Config_.Transport.ToString)
    End Function
    Public Shared Function Factory(hostName As String, useSsl As Boolean, restTimeoutMs As Integer) As CUpgradeClient
        Select Case Config_.Transport
            Case ETransport.Rest : Return New CUpgradeClient_Rest(hostName, useSsl, restTimeoutMs)
            Case ETransport.WCF : Return New CUpgradeClient_Wcf(Config_.Endpoint, hostName, useSsl) 'todo: timeout?

            Case ETransport.CmdLineExe : Throw New Exception(CMD_LINE_ERR)
            Case ETransport.InProcess : Throw New Exception(INPROC_ERROR)
        End Select
        Throw New Exception("Not implemented: " & Config_.Transport.ToString)
    End Function
    Public Shared Function Factory(hostName As String, useSsl As Boolean, wcfEndpoint As String) As CUpgradeClient
        Select Case Config_.Transport
            Case ETransport.Rest : Return New CUpgradeClient_Rest(hostName, useSsl)
            Case ETransport.WCF : Return New CUpgradeClient_Wcf(wcfEndpoint, hostName, useSsl)

            Case ETransport.CmdLineExe : Throw New Exception(CMD_LINE_ERR)
            Case ETransport.InProcess : Throw New Exception(INPROC_ERROR)
        End Select
        Throw New Exception("Not implemented: " & Config_.Transport.ToString)
    End Function
    Public Shared Function Factory(hostName As String, useSsl As Boolean, password As IConfig) As CUpgradeClient
        Select Case Config_.Transport
            Case ETransport.Rest : Return New CUpgradeClient_Rest(hostName, useSsl, password)
            Case ETransport.WCF : Return New CUpgradeClient_Wcf(Config_.Endpoint, hostName, useSsl, password)

            Case ETransport.CmdLineExe : Throw New Exception(CMD_LINE_ERR)
            Case ETransport.InProcess : Throw New Exception(INPROC_ERROR)
        End Select
        Throw New Exception("Not implemented: " & Config_.Transport.ToString)
    End Function
    Public Shared Function Factory(hostAndPassword As IConfig_Client) As CUpgradeClient
        Select Case Config_.Transport
            Case ETransport.Rest : Return New CUpgradeClient_Rest(hostAndPassword)
            Case ETransport.WCF : Return New CUpgradeClient_Wcf(hostAndPassword.Endpoint, hostAndPassword.HostName, hostAndPassword.UseSsl, hostAndPassword)

            Case ETransport.CmdLineExe : Throw New Exception(CMD_LINE_ERR)
            Case ETransport.InProcess : Throw New Exception(INPROC_ERROR)
        End Select
        Throw New Exception("Not implemented: " & Config_.Transport.ToString)
    End Function
#End Region

#Region "Shared - Config"
	Public Shared ReadOnly Property Config_ As CUpgradeClient_Config
		Get
			Return CUpgradeClient_Config.Shared
		End Get
	End Property
#End Region


#Region "New Logic"
	Private Shared LAST_REPORT_ID As Integer = 0

    Public Function FirstReport() As Integer
        Return FirstReport(Config_.Identity)
    End Function
    Public Function OnAppStarted() As Boolean
        Return OnAppStarted(0, False, False, False)
    End Function
#End Region

#Region "New Logic - Do Upgrade"
    Private Function OnAppStarted(loops As Integer, fullSchemaInfo As Boolean, fullHashInfo As Boolean, fullBinaryInfo As Boolean) As Boolean
        If loops > 5 Then Return True

        Dim info As COnAppStarted = GetMyInfo(fullSchemaInfo, fullHashInfo, fullBinaryInfo)
        Dim r As CUpgradeResponse = Me.OnAppStarted(info)

        'Perform any upgrade actions
        Dim repeat As Boolean = r.SendBinaries OrElse r.SendSchemaInfo
        Dim msg As New List(Of String)

        'Upgrade: Database
        If Not IsNothing(r.RunScripts) Then
            For Each i As String In r.RunScripts
                Try
                    CDataSrc.Default.ExecuteNonQuery(i)
                Catch ex As Exception

                End Try
                repeat = True
            Next
        End If

        'Upgrade: Config
        If Not IsNothing(r.YourName) Then
            Dim y As CYourInfo = r.YourName

            'Config settings: IDx3
            Dim id As CIdentity = y.Id
			If id.InstanceId <> Config_.InstanceId Then
				Config_.WriteConfigSetting_InstanceId(id.InstanceId, True)
				repeat = True
			End If
			If id.HostName <> Config_.InstanceHostName Then
				Config_.WriteConfigSetting_InstanceHostName(id.HostName, True)
				repeat = True
			End If
			If id.ClientName <> Config_.ClientName Then
				Config_.WriteConfigSetting_ClientName(id.ClientName, True)
				repeat = True
			End If
			If id.ClientSuffix <> Config_.ClientSuffix Then
				Config_.WriteConfigSetting_ClientSuffix(id.ClientSuffix, True)
				repeat = True
			End If
			If id.AppId <> Config_.AppId Then
                Config_.WriteConfigSetting_AppId(id.AppId)
                repeat = True
            End If

            'Config settings: Filter x1
            Dim f As CFilter = y.Filter
            If f.IgnoreExtensions <> Config_.ExtensionsToIgnore Then
                Config_.WriteConfigSetting_ExtensionsToIgnore(f.IgnoreExtensions)
                repeat = True
            End If


            'Temp setting (until appStopped or upgrade)
            LAST_REPORT_ID = y.LastReportId
        End If

        'Upgrade: Filesystem
        If Not IsNothing(r.UpgradeTo) Then
            If r.UpgradeTo.Count > 0 Then repeat = True

            Dim root As String = RootDir()
            For Each i As CFileNameAndContent In r.UpgradeTo
                Dim path As String = root & i.Name
                Dim fold As String = IO.Path.GetDirectoryName(path)
                If Not IO.Directory.Exists(fold) Then IO.Directory.CreateDirectory(fold)
                IO.File.WriteAllBytes(path, i.Content)
            Next
        End If

        'Repeat Call-back
        If repeat Then
            OnAppStarted(loops + 1, r.SendSchemaInfo, r.SendFolderHash, r.SendBinaries)
            Return True
        End If

        Return False
    End Function
    Private Function RootDir() As String
        If IsNothing(HttpContext.Current) OrElse IsNothing(HttpContext.Current.Server) Then
            Return My.Application.Info.DirectoryPath
        Else
            Return HttpContext.Current.Server.MapPath("~/")
        End If
    End Function
    Private Function GetMyInfo(includeSchema As Boolean, includeHashes As Boolean, includeBinaries As Boolean) As COnAppStarted
        Dim myInfo As New COnAppStarted

		'id
		myInfo.Id = Config_.GetId

		'filters
		myInfo.Filters = New CFilter(Config_.ExtensionsToIgnore, True)


        Dim folder As New CFolderHash(RootDir, Config_.ExtensionsToIgnore_AsArray, True)
        Dim schema As New CSchemaInfo(CDataSrc.Default)

        'hashed info
        myInfo.BinMd5 = folder.Hash
        myInfo.SchemaMd5 = schema.MD5

        'Full info
        If includeSchema Then myInfo.Schema = schema
        If includeHashes Then myInfo.Folder = folder
        If includeBinaries Then myInfo.BinFiles = New CFilesList(RootDir, Config_.ExtensionsToIgnore_AsArray, True)

        Return myInfo
    End Function
#End Region


#Region "Public Interface (combines all input parameters into a single object)"
	Public Function FirstReport(id As CIdentity) As Integer Implements IUpgrade.FirstReport
        Return CSerialise.Proto.AsInt(Invoke(EUpgrade.FirstReport, id))
    End Function

    Public Function OnAppStarted(myInfo As COnAppStarted) As CUpgradeResponse Implements IUpgrade.OnAppStarted
        Return CSerialise.Proto.Deserialise_(Of CUpgradeResponse)(Invoke(EUpgrade.OnAppStarted, myInfo))
    End Function

    Public Sub OnAppStopped(myInfo As COnAppStopped) Implements IUpgrade.OnAppStopped
        Invoke(EUpgrade.OnAppStopped)
    End Sub
#End Region

#Region "Shadows: Integer-enum => Custom Enum (EUpgrade)"
    Protected Shadows Function Invoke(enum_ As EUpgrade, ParamArray params As Object()) As Byte() 'most commonly-used overload
        Return MyBase.Invoke(enum_, params)
    End Function
    Protected Shadows Function Invoke(enum_ As EUpgrade, gzip As EGzip, ParamArray params As Object()) As Byte() 'occasional control of compression at the method-level
        Return MyBase.Invoke(enum_, gzip, params)
    End Function
    Protected Shadows Function Invoke(enum_ As EUpgrade, gzip As EGzip, formatIn As ESerialisation, ParamArray params As Object()) As Byte() 'rarely used (encryption control)
        Return MyBase.Invoke(enum_, gzip, formatIn, params)
    End Function
    Protected Shadows Function Invoke(enum_ As EUpgrade, gzip As EGzip, formatIn As ESerialisation, formatOut As ESerialisation, ParamArray params As Object()) As Byte() 'rarely used (encryption control)
        Return MyBase.Invoke(enum_, gzip, formatIn, formatOut, params)
    End Function
    Protected Shadows Function Invoke(enum_ As EUpgrade, gzip As EGzip, formatIn As ESerialisation, formatOut As ESerialisation, encryption As EEncryption, ParamArray params As Object()) As Byte()
        Return MyBase.Invoke(enum_, gzip, formatIn, formatOut, encryption, params) 'lowest-level (serialisation option is rarely used)
    End Function


#End Region

End Class

